use std::{collections::HashMap, fs::{File, OpenOptions}, io::{Read, Write}, process::Command};

use dotenv::dotenv;
use structopt::StructOpt;
use toml::Table;
#[derive(Debug,StructOpt)]
#[structopt(name="zman",about="command line tools")]
pub struct Opt {
    /// select run command
    pub command:String,
    /// the require param for running command line. 
    pub param:Vec<String>,
}


fn main() {
    println!(r"
    _                    _   
   | |                  | |  
____  _ __ ___     __ _   _ __      ___  | |_    __ _   _ __  | |_ 
|_  / | '_ ` _ \   / _` | | '_ \    / __| | __|  / _` | | '__| | __|
/ /  | | | | | | | (_| | | | | |   \__ \ | |_  | (_| | | |    | |_ 
/___| |_| |_| |_|  \__,_| |_| |_|   |___/  \__|  \__,_| |_|     \__|                             
");
    dotenv().ok(); 

    let opt = Opt::from_args();

    // println!("{:?}",opt);
//    let path = std::env::args().nth(0).expect("parse path err");
    // let command = std::env::args().nth(1).expect("no command given");
    // let args = std::env::args().nth(2).expect("no args given");
    // let mut line = std::env::var(opt.command.clone())
        // .expect(&("don't find command config for".to_owned()+&opt.command));
    let mut line = load_config(opt.command.clone())
        .expect(&("no such command:".to_owned()+&opt.command));
    line = (&line[1..line.len()-1]).to_string();

    if line.contains("@") {
         line = line.replace("@", opt.param.get(0).unwrap());
    }

    let _ :Vec<i32> = line.split("&&")
    .map(|c|{
        println!("${}",c);
        let out = Command::new(get_target_os().0)
            .arg(get_target_os().1).arg(c).output().expect("run command line is failed!");
        let stdout = String::from_utf8_lossy(&out.stdout);
        println!("{}",stdout);
        1
    }).collect();
    // let out = Command::new(line).output().expect("run command line is failed!");
    // let stdout = String::from_utf8_lossy(&out.stdout);
    println!(r"
    _ 
   | |
____  _ __ ___     __ _   _ __       ___   _ __     __| |
|_  / | '_ ` _ \   / _` | | '_ \     / _ \ | '_ \   / _` |
/ /  | | | | | | | (_| | | | | |   |  __/ | | | | | (_| |
/___| |_| |_| |_|  \__,_| |_| |_|    \___| |_| |_|  \__,_|      
");
}


pub fn get_target_os() -> (String,String){
    let mut os_list = HashMap::new();
    os_list.insert("windows", "powershell.exe");
    os_list.insert("linux", "bash");
    os_list.insert("macos", "bash");
    
    let target_os = os_list.get(std::env::consts::OS).unwrap();
    if *target_os=="powershell.exe" {
        (target_os.to_string(),String::from("/c"))
    }else {
        (target_os.to_string(),String::from("-c"))
    }    
}

// read user custom config file
pub fn load_config(command:String) -> Option<String> {
   // define config file path
   let config_file_path = match std::env::consts::OS {
        "windows" => "C:\\Users\\zeng\\.config\\zman.toml",
        _ => "~/.config/zman.toml"
   };
   let mut config_file = match File::open(config_file_path) {
       Ok(f) => f,
       Err(_e) => init_config_file(config_file_path)
   }; 

   let mut str_val = String::new();
   match config_file.read_to_string(&mut str_val) {
       Ok(s) => s,
       Err(e) => panic!("ERROR Reading file: {}",e)
   };
   let value:Table = str_val.parse::<Table>().unwrap();
//    println!("{}",value);
   Some(value[&command].to_string())
}

fn init_config_file(path: &str) -> File {
    File::create(path).unwrap();
    let mut file = OpenOptions::new().append(true).open(path).unwrap();
    let _ = file.write_all(("gg=\"git add .&&git commit -m ".to_owned()+("'@'")+"&&git push\"\nga=\"git add .\"\ngp=\"git pull\"\nll=\"ls\"").as_bytes());
    File::open(path).unwrap()
}

#[test]
fn test_os(){
    println!("{}:{}",get_target_os().0,get_target_os().1);
}

#[test]
fn test_load_config(){
    assert_eq!(load_config("ll".to_string()),Some("\"ls -al\"".to_string()));
}